-- @Author: qingant

module(..., package.seeall)

local ffi = require("ffi")
local lua = require(_PACKAGE .. "_lcapi")

ffi.cdef[[

   int usleep(int useconds);
   typedef int64_t pthread_t;
   typedef struct pthread_attr_t pthread_attr_t;
   typedef void *(*thread_routine)(void*);
   int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr,
         void *(*start_routine)(void *), void *restrict arg);
   void free(void *ptr);
   void *malloc(size_t size);
]]

function usleep(us)
    ffi.C.usleep(us)
end


local thread = {}

function thread:new()
	local o = {}
	setmetatable(o, self)
	self.__index = self
	return o
end

function thread:init(f)
    self._routine = f
    self._state = ffi.C.luaL_newstate()
    ffi.C.luaL_openlibs(self._state)
    return self
end

function thread:__gc()
end
local function boot(s, name)
    local thread = require(name)
    local ffi = require("ffi")
    local id = ffi.gc(ffi.C.malloc(8), ffi.C.free)
    local f = loadstring(s)
    local rt = ffi.C.pthread_create(id, nil, f, nil)
end
function thread:start()
    local boot_s = string.dump(boot)
    ffi.C.luaL_loadbuffer(self._state, boot_s, #boot_s, "boot")

    local routine_s = string.dump(self._routine)
    ffi.C.lua_pushlstring(self._state, routine_s, #routine_s)

    ffi.C.lua_pushstring(self._state, _NAME)
    local ret = ffi.C.lua_pcall(self._state, 2, 0, 0)

    -- TODO: pcall error handling
    assert(ret==0, "pcall failed")
    return self
end


function spawn(f)
    return thread:new():init(f):start()
end

